#include "rawdata.h"
#include "bitstream.h"
#include "log.h"
#include <algorithm>
#include <iostream>
#include <sstream>

using namespace std;

RawData::RawData(string &path, int wordSize) :
		path(path), wordSize(wordSize)
{
}

RawData::RawData(string &path, int wordSize, std::vector<int> &dictionary,
		int lastSize) :
		path(path), wordSize(wordSize), dictionary(dictionary), lastSize(
				lastSize)
{
}

RawDataInput::RawDataInput(string &path, int wordSize) :
		RawData(path, wordSize), dictBuilder(1 << wordSize)
{
	ostringstream ss;
	ss << "Reading file '" << path << "' (W=" << wordSize << ")...";
	logger.info(ss.str(), true);
	fstream file(path.c_str(), ios_base::in | ios_base::binary);
	BitStream bs(file);
	file.close();

	while (!bs.END)
	{
		data.push_back(bs.get(wordSize));
		logger.progress(data.size() * wordSize, bs.length() * 8);
	}
	lastSize = (bs.END != 0) ? bs.END : wordSize;

	logger.info("Creating dictionary...");
	buildDictionary();
	logger.append("done\n");
}

void RawDataInput::buildDictionary()
{
	for (unsigned int i = 0; i < data.size(); i++)
	{
		int word = data[i];
		dictBuilder[word].word = word;
		dictBuilder[word].count++;
	}
	sort(dictBuilder.begin(), dictBuilder.end(), cmp);

	for (unsigned int i = 0; i < dictBuilder.size(); i++)
	{
		if (dictBuilder[i].count == 0)
			break;
		dictionary.push_back(dictBuilder[i].word);
	}

	index = vector<int>(1 << wordSize, -1);
	for (unsigned int i = 0; i < dictionary.size(); i++)
	{
		index[dictionary[i]] = i;
	}
}

int RawDataInput::length()
{
	return data.size();
}

int RawDataInput::get(int i)
{
	return index[data[i]];
}

RawDataOutput::RawDataOutput(std::string &path, int wordSize,
		std::vector<int> &dictionary, int lastSize) :
		RawData(path, wordSize, dictionary, lastSize)
{
}

void RawDataOutput::put(int symbol)
{
	data.push_back(symbol);
}

void RawDataOutput::commit()
{
	ostringstream ss;
	ss << "Writing file '" << path << "' (W=" << wordSize << ")...";
	logger.info(ss.str(), true);
	BitStream bs;
	unsigned int i;
	for (i = 0; i < data.size() - 1; i++)
	{
		bs.put(dictionary[data[i]], wordSize);
		logger.progress(i + 1, data.size() * 2);
	}
	bs.put(dictionary[data[i]], lastSize);

	ostream* file;
	if (path.empty())
		file = &cout;
	else
		file = new fstream(path.c_str(), ios_base::out | ios_base::binary);

	for (int i = 0; i < bs.length(); i++)
	{
		file->put(bs[i]);
		logger.progress(i + 1 + bs.length(), bs.length() * 2);
	}
	if (!path.empty())
	{
		((fstream*) file)->close();
		delete file;
	}
}
